﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;

namespace WindowsFormsApplication1
{
    class CompilerErrorArg : EventArgs
    {
        public string Data;

        public CompilerErrorArg(string d) { Data = d; }
    };

    class Builder
    {
        private List<string> output;
        private List<Process> compileProcesses;
        private List<Process> linkProcesses;
        private Project project;
        private ISynchronizeInvoke invoke;

        public delegate void CompilationDoneHandler(object sender, EventArgs e);
        public delegate void LinkerDoneHandler(object sender, EventArgs e);
        public event CompilationDoneHandler CompilationDone;
        public event LinkerDoneHandler LinkerDone;

        public delegate void CompilerErrorHandler(object sender, CompilerErrorArg e);
        public event CompilerErrorHandler CompilerError;

        internal Builder(Project p, ISynchronizeInvoke i)
        {
            invoke = i;
            output = new List<string>();
            compileProcesses = new List<Process>();
            linkProcesses = new List<Process>();
            
            project = p;
            CompilationDone += new CompilationDoneHandler(Builder_CompilationDone);
        }

        void Builder_CompilationDone(object sender, EventArgs e)
        {
            string output = "-Wa-l -Wl-m -Wl-j -DUSE_SFR_FOR_REG -o game.gb";
            foreach (ProjectFile file in project.Files)
                output += " \"" + file.OutputFile + "\"";

            linkProcesses.Add(CreateProcess(output));

            StartList(linkProcesses);
        }

        public void Build()
        {
            foreach(ProjectFile file in project.Files)
                QueueFileForCompilation(file);

            StartList(compileProcesses);
        }

        private void StartList(List<Process> list)
        {
            foreach (Process p in list)
            {
                p.Start();
                p.BeginErrorReadLine();
                p.BeginOutputReadLine();
            }
        }

        private void QueueFileForCompilation(ProjectFile file)
        {
            Process p = CreateProcess(file.Commandline + " -c -o \"" + file.OutputFile + "\" \"" + file.Path + "\"");
            compileProcesses.Add(p);
        }

        private Process CreateProcess(string args)
        {
            Process p = new Process();

            p.StartInfo.FileName = @"C:\Users\Jasper\Documents\gb\gbdk\bin\lcc.exe";
            p.StartInfo.Arguments = args;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.CreateNoWindow = true;
            p.SynchronizingObject = invoke;
            p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
            p.ErrorDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
            p.Exited += new EventHandler(p_Exited);
            p.EnableRaisingEvents = true;

            return p;
        }

        void p_Exited(object sender, EventArgs e)
        {
            Process p = sender as Process;
            if (compileProcesses.Contains(p))
            {
                compileProcesses.Remove(p);

                if (compileProcesses.Count == 0)
                    if (CompilationDone != null)
                        CompilationDone(this, new EventArgs());
            }
            else if (linkProcesses.Contains(p))
            {
                linkProcesses.Remove(p);

                if (linkProcesses.Count == 0)
                    if (LinkerDone != null)
                        LinkerDone(this, new EventArgs());
            }
        }

        void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (!String.IsNullOrEmpty(e.Data))
            {
                if (CompilerError != null)
                    CompilerError(this, new CompilerErrorArg(e.Data.Replace("\\\\", "\\")));
            }
        }
    }
}
